0, 'errors' => 0, 'error_details' => []]; // Get classes and academic years for dropdowns function getClasses() { global $DBcon; try { $sql = "SELECT classid FROM class ORDER BY classid"; $stmt = $DBcon->prepare($sql); $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_ASSOC); } catch (PDOException $e) { error_log("Get classes error: " . $e->getMessage()); return []; } } function getAcademicYears() { global $DBcon; try { $sql = "SELECT academic_year FROM calender ORDER BY academic_year DESC"; $stmt = $DBcon->prepare($sql); $stmt->execute(); return $stmt->fetchAll(PDO::FETCH_ASSOC); } catch (PDOException $e) { error_log("Get academic years error: " . $e->getMessage()); return []; } } $classes = getClasses(); $academic_years = getAcademicYears(); // Handle file upload and import if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['import'])) { // Validate CSRF token if (!validate_csrf_token($_POST['csrf_token'])) { $message = 'Security token validation failed.'; $message_type = 'error'; } else { // Validate required fields $class_id = sanitize_input($_POST['class_id']); $admin_year = sanitize_input($_POST['admin_year']); if (empty($class_id) || empty($admin_year)) { $message = 'Please select both class and academic year.'; $message_type = 'error'; } elseif (!isset($_FILES['excel_file']) || $_FILES['excel_file']['error'] !== UPLOAD_ERR_OK) { $message = 'Please select a valid Excel file to upload.'; $message_type = 'error'; } else { // Process the Excel file $result = processExcelUpload($_FILES['excel_file'], $class_id, $admin_year); if ($result['success']) { $message = "Import completed successfully! {$result['stats']['success']} students imported, {$result['stats']['errors']} errors."; $message_type = 'success'; $import_stats = $result['stats']; } else { $message = $result['message']; $message_type = 'error'; if (isset($result['stats'])) { $import_stats = $result['stats']; } } } } } function processExcelUpload($file, $class_id, $admin_year) { global $DBcon; $allowed_types = [ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-excel' ]; // Validate file type if (!in_array($file['type'], $allowed_types)) { return [ 'success' => false, 'message' => 'Invalid file type. Please upload an Excel file (.xlsx).' ]; } try { $students_data = parseXLSXFile($file['tmp_name']); if (empty($students_data)) { return [ 'success' => false, 'message' => 'No valid student data found in the file or unable to parse the file.' ]; } $stats = ['success' => 0, 'errors' => 0, 'error_details' => []]; $row_count = 0; // Start transaction for atomic operations $DBcon->beginTransaction(); foreach ($students_data as $row_index => $row) { $row_count++; // Skip header row (assuming first row is header) if ($row_count === 1) continue; // Skip empty rows if (empty($row['fullname']) && empty($row['regno'])) continue; $fullname = sanitize_input($row['fullname']); $regno = sanitize_input($row['regno']); // Validate required fields if (empty($fullname) || empty($regno)) { $stats['errors']++; $stats['error_details'][] = "Row $row_count: Missing name or registration number"; continue; } // Check if registration number already exists if (registrationNumberExists($regno)) { $stats['errors']++; $stats['error_details'][] = "Row $row_count: Registration number '$regno' already exists"; continue; } // Insert student if (insertStudent($fullname, $regno, $class_id, $admin_year)) { $stats['success']++; } else { $stats['errors']++; $stats['error_details'][] = "Row $row_count: Failed to insert student '$fullname'"; } } // Commit transaction $DBcon->commit(); return [ 'success' => true, 'stats' => $stats ]; } catch (Exception $e) { // Rollback transaction on error $DBcon->rollBack(); error_log("Excel import error: " . $e->getMessage()); return [ 'success' => false, 'message' => 'Error processing Excel file: ' . $e->getMessage() ]; } } function parseXLSXFile($filename) { $students = []; // Check if ZIP extension is available if (!class_exists('ZipArchive')) { throw new Exception("ZIP extension is required for XLSX file processing. Please enable php_zip extension in your PHP configuration."); } $temp_dir = sys_get_temp_dir() . '/xlsx_extract_' . uniqid(); if (!mkdir($temp_dir, 0755, true)) { throw new Exception("Could not create temporary directory for XLSX extraction."); } $zip = new ZipArchive(); if ($zip->open($filename) !== TRUE) { deleteDirectory($temp_dir); throw new Exception("Could not open XLSX file. File may be corrupted or not a valid XLSX file."); } try { // Extract to temporary directory $zip->extractTo($temp_dir); $zip->close(); // Parse shared strings $sharedStrings = parseSharedStrings($temp_dir); // Parse worksheet data $students = parseWorksheet($temp_dir, $sharedStrings); } catch (Exception $e) { deleteDirectory($temp_dir); throw $e; } deleteDirectory($temp_dir); return $students; } function parseSharedStrings($temp_dir) { $sharedStrings = []; $sharedStringsFile = $temp_dir . '/xl/sharedStrings.xml'; if (!file_exists($sharedStringsFile)) { return $sharedStrings; } $content = file_get_contents($sharedStringsFile); // Extract all text elements from shared strings preg_match_all('/]*>(.*?)<\/t>/s', $content, $matches); foreach ($matches[1] as $index => $text) { $sharedStrings[$index] = html_entity_decode($text, ENT_QUOTES | ENT_XML1, 'UTF-8'); } return $sharedStrings; } function parseWorksheet($temp_dir, $sharedStrings) { $students = []; $sheetFile = $temp_dir . '/xl/worksheets/sheet1.xml'; if (!file_exists($sheetFile)) { throw new Exception("Could not find worksheet data in the XLSX file."); } $content = file_get_contents($sheetFile); // Extract all rows preg_match_all('/]*>(.*?)<\/row>/s', $content, $rowMatches); foreach ($rowMatches[1] as $rowIndex => $rowContent) { if ($rowIndex == 0) continue; // Skip header row $rowData = parseRowData($rowContent, $sharedStrings); // Excel file structure: // Column A: Full Name // Column B: Registration Number if (count($rowData) >= 2) { $student = [ 'fullname' => $rowData['A'] ?? '', 'regno' => $rowData['B'] ?? '' ]; // Only add if we have both name and regno if (!empty(trim($student['fullname'])) && !empty(trim($student['regno']))) { $students[] = $student; } } } return $students; } function parseRowData($rowContent, $sharedStrings) { $rowData = []; // Extract all cells in the row with their column references preg_match_all('/]*r="([A-Z])(\d+)"[^>]*>(.*?)<\/c>/s', $rowContent, $cellMatches, PREG_SET_ORDER); foreach ($cellMatches as $cellMatch) { $column = $cellMatch[1]; // Column letter (A, B, C, etc.) $cellContent = $cellMatch[3]; // Extract cell value preg_match('/]*>(.*?)<\/v>/s', $cellContent, $valueMatch); if (!isset($valueMatch[1])) { $rowData[$column] = ''; continue; } $cellValue = trim($valueMatch[1]); // Check if cell uses shared string if (strpos($cellMatch[0], 't="s"') !== false) { // Value is an index into shared strings $stringIndex = intval($cellValue); $rowData[$column] = $sharedStrings[$stringIndex] ?? ''; } else { // Direct value $rowData[$column] = $cellValue; } } return $rowData; } function deleteDirectory($dir) { if (!file_exists($dir)) return true; if (!is_dir($dir)) return unlink($dir); foreach (scandir($dir) as $item) { if ($item == '.' || $item == '..') continue; if (!deleteDirectory($dir . DIRECTORY_SEPARATOR . $item)) { return false; } } return rmdir($dir); } function registrationNumberExists($regno) { global $DBcon; try { $sql = "SELECT COUNT(*) FROM students_info WHERE regno = :regno"; $stmt = $DBcon->prepare($sql); $stmt->execute([':regno' => $regno]); return $stmt->fetchColumn() > 0; } catch (PDOException $e) { error_log("Check regno error: " . $e->getMessage()); return true; // Return true to prevent insertion on error } } function insertStudent($fullname, $regno, $class_id, $admin_year) { global $DBcon; try { $sql = "INSERT INTO students_info (fullname, regno, class_id, admin_year) VALUES (:fullname, :regno, :class_id, :admin_year)"; $stmt = $DBcon->prepare($sql); return $stmt->execute([ ':fullname' => $fullname, ':regno' => $regno, ':class_id' => $class_id, ':admin_year' => $admin_year ]); } catch (PDOException $e) { error_log("Insert student error: " . $e->getMessage()); return false; } } // Generate CSRF token $csrf_token = generate_csrf_token(); ?> Import Students - School Admin
Warning: ZIP extension is not enabled on your server. Please contact your hosting provider to enable the php_zip extension.

Import Students from Excel

Important Instructions:
  • Excel file must be in .xlsx format
  • First row should contain column headers
  • Column A: Student Full Name
  • Column B: Registration Number
  • Maximum file size: 10MB
Drag & Drop your Excel file here

or click to browse

No file selected
Back to Students
0 && !empty($import_stats['error_details'])): ?>
Import Error Details:

Excel Format

Required Columns:
Column Content Required
A Full Name Yes
B Registration Number Yes
Sample Data:
A B
John Doe REG001
Jane Smith REG002
... ...
Copyright © 2018. All rights reserved. Hand-crafted & made with